﻿/*版权所有（C）GDNet框架
*
*该软件按“原样”提供，不提供任何形式的明示或暗示担保，
*无论是由于软件，使用或其他方式产生的，侵权或其他形式的任何索赔，损害或其他责任，作者或版权所有者概不负责。
*
*允许任何人出于任何目的使用本框架，
*包括商业应用程序，并对其进行修改和重新发布自由
*
*受以下限制：
*
*  1. 不得歪曲本软件的来源；您不得
*声称是你写的原始软件。如果你用这个框架
*在产品中，产品文档中要确认感谢。
*  2. 更改的源版本必须清楚地标记来源于GDNet框架，并且不能
*被误传为原始软件。
*  3. 本通知不得从任何来源分发中删除或更改。
*/
using System;
using System.Net;
using System.Threading;
using System.Reflection;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Collections.Concurrent;
using Net.Share;
using Net.System;
using Net.Helper;
using Net.Adapter;
using Net.Config;
using Net.Event;
using Cysharp.Threading.Tasks;
#if WINDOWS
using Microsoft.Win32;
#endif

namespace Net.Server
{
    /// <summary>
    /// 网络服务器核心基类（非泛型）
    /// </summary>
    public abstract class ServerBase
    {
        #region 属性
        /// <summary>
        /// (分布式)服务器名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// (分布式)主机名称
        /// </summary>
        public string HostName { get; set; }
        /// <summary>
        /// 服务器套接字
        /// </summary>
        public Socket Server { get; protected set; }
        /// <summary>
        /// io完成端口对象
        /// </summary>
        public SocketAsyncEventArgs ServerArgs { get; protected set; }
        /// <summary>
        /// 服务器端口
        /// </summary>
        public int Port { get; protected set; }
        /// <summary>
        /// 服务器是否处于运行状态, 如果服务器套接字已经被释放则返回False, 否则返回True. 当调用Close方法后将改变状态
        /// </summary>
        public bool IsRunServer { get; set; }
        /// <summary>
        /// 服务器配置
        /// </summary>
        public ServerConfig Config { get; set; } = new();
        /// <summary>
        /// 流量统计
        /// </summary>
        protected NetworkTrafficStatistics trafficStatistics = new();
        /// <summary>
        /// 同步锁对象
        /// </summary>
        protected readonly object SyncRoot = new();
        /// <summary>
        /// 服务器线程管理
        /// </summary>
        protected internal Dictionary<string, Thread> ServerThreads = new();
        /// <summary>
        /// 数据包适配器
        /// </summary>
        public IPackageAdapter PackageAdapter { get; set; } = new PackageAdapter();
        /// <summary>
        /// 序列化适配器
        /// </summary>
        public ISerializeAdapter SerializeAdapter { get; set; } = new SerializeDefaultAdapter();
        /// <summary>
        /// 服务器Rpc适配器
        /// </summary>
        public IRpcAdapter RpcAdapter { get; set; } = new NormalRpcAdapter() { Mode = AdapterMode.Server };
        /// <summary>
        /// 远程调用工厂接口
        /// </summary>
        public IRpcModelFactory RpcUnitFactory { get; set; } = new RpcModelFactory();
        protected int performance;
        /// <summary>
        /// 服务器性能模式
        /// </summary>
        public Performance Performance
        {
            get => (Performance)performance;
            set => performance = (int)value;
        }
        /// <summary>
        /// 排队队列
        /// </summary>
        protected ConcurrentQueue<ClientPeer> QueueUp = new();
        #endregion

        #region 服务器事件处理
        /// <summary>
        /// 当统计网络流量时触发
        /// </summary>
        public NetworkDataTraffic OnNetworkDataTraffic { get; set; }
        #endregion

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="port">端口</param>
        public abstract void Start(int port = 9543);

        /// <summary>
        /// 初始化调用, 主动调用
        /// </summary>
        /// <param name="args"></param>
        public virtual void Initialize(params object[] args) { }

        /// <summary>
        /// 设置服务器配置
        /// </summary>
        /// <param name="config"></param>
        public virtual void SetConfig(ServerConfig config)
        {
            if (config == null)
                return;
            Config = config;
        }

        /// <summary>
        /// 关闭指定客户端, 并移出客户端列表
        /// </summary>
        /// <param name="remotePoint"></param>
        public void CloseClient(EndPoint remotePoint) => RemoveClient(remotePoint);

        /// <summary>
        /// 移除客户端
        /// </summary>
        /// <param name="remotePoint"></param>
        /// <returns></returns>
        public abstract void RemoveClient(EndPoint remotePoint);

        /// <summary>
        /// 提供分布式统一处理接口
        /// </summary>
        /// <param name="self"></param>
        /// <param name="model"></param>
        public virtual UniTask OnMessageHandler(ClientUnitBase self, IRpcModel model) => UniTask.CompletedTask;

        /// <summary>
        /// 添加适配器
        /// </summary>
        /// <param name="adapter"></param>
        public void AddAdapter(IAdapter adapter)
        {
            if (adapter is ISerializeAdapter ser)
                AddAdapter(AdapterType.Serialize, ser);
            else if (adapter is IRpcAdapter rpc)
                AddAdapter(AdapterType.Rpc, rpc);
            else if (adapter is IPackageAdapter package)
                AddAdapter(AdapterType.Package, package);
            else throw new Exception("无法识别的适配器!");
        }

        /// <summary>
        /// 添加适配器
        /// </summary>
        /// <param name="type"></param>
        /// <param name="adapter"></param>
        public void AddAdapter(AdapterType type, IAdapter adapter)
        {
            switch (type)
            {
                case AdapterType.Serialize:
                    SerializeAdapter = (ISerializeAdapter)adapter;
                    break;
                case AdapterType.Rpc:
                    RpcAdapter = (IRpcAdapter)adapter;
                    RpcAdapter.Mode = AdapterMode.Server;
                    break;
                case AdapterType.Package:
                    PackageAdapter = (IPackageAdapter)adapter;
                    break;
            }
        }
    }

    /// <summary>
    /// 网络服务器核心基类 2023.11.13
    /// <para>Player:当有客户端连接服务器就会创建一个Player对象出来, Player对象和XXXClient是对等端, 每当有数据处理都会通知Player对象. </para>
    /// </summary>
    public abstract partial class ServerBase<Client> : ServerBase where Client : ClientPeer, new()
    {
        #region 属性
        /// <summary>
        /// 所有客户端列表
        /// </summary>
        public ConcurrentDictionary<long, Client> Clients { get; private set; } = new();
        /// <summary>
        /// 网络线程管线处理池
        /// </summary>
        protected IThreadPipeline ThreadPool { get; set; }
        #endregion

        #region 服务器事件处理
        /// <summary>
        /// 当客户端连接
        /// </summary>
        public Action<Client> OnConnecting { get; set; }
        /// <summary>
        /// 当接收到消息时处理事件
        /// </summary>
        public OnReceiveDataHandle<Client> OnReceiveData { get; set; }
        /// <summary>
        /// 当断开连接
        /// </summary>
        public Action<Client> OnDisconnect { get; set; }
        /// <summary>
        /// 当执行调用远程过程方法时触发
        /// </summary>
        public OnReceiveDataHandle<Client> OnRpcInvoke { get; set; }
        #endregion

        /// <summary>
        /// 构造网络服务器函数
        /// </summary>
        public ServerBase()
        {
        }

        #region 索引
        /// <summary>
        /// 玩家索引
        /// </summary>
        /// <param name="remotePoint"></param>
        /// <returns></returns>
        public Client this[EndPoint remotePoint] => Clients[remotePoint.GetRemoteId()];

        /// <summary>
        /// uid索引
        /// </summary>
        /// <param name="uid"></param>
        /// <returns></returns>
        public Client this[long uid] => Clients[uid];

        #endregion

        #region 重写方法
        /// <summary>
        /// 当开始启动服务器
        /// </summary>
        protected virtual void OnStarting() { NDebug.Log($"{Name}服务器开始运行..."); }

        /// <summary>
        /// 当服务器启动完毕
        /// </summary>
        protected virtual void OnStartupCompleted() { NDebug.Log($"{Name}服务器启动成功!"); }

        /// <summary>
        /// 当有客户端连接
        /// </summary>
        /// <param name="client">客户端套接字</param>
        protected virtual void OnHasConnect(Client client) => NDebug.Log($"[{client}]连接服务器!");

        /// <summary>
        /// 当客户端连接中断, 此时还会等待客户端重连, 如果10秒后没有重连上来就会真的断开
        /// </summary>
        /// <param name="client"></param>
        protected virtual void OnConnectLost(Client client) => NDebug.Log($"[{client}]连接中断!");

        /// <summary>
        /// 当断线重连成功触发
        /// </summary>
        /// <param name="client"></param>
        public virtual void OnReconnecting(Client client) => NDebug.Log($"[{client}]断线重连成功");

        /// <summary>
        /// 当服务器判定客户端为断线或连接异常时，移除客户端时调用
        /// </summary>
        /// <param name="client">要移除的客户端</param>
        protected virtual void OnRemoveClient(Client client) { NDebug.Log($"[{client}]断开连接!"); }

        /// <summary>
        /// 当执行Rpc(远程过程调用函数)时调用, 如果想提升服务器Rpc调用性能(默认反射调用), 可以重写此方法, 指定要调用的方法
        /// </summary>
        /// <param name="client">客户端</param>
        /// <param name="model">数据模型</param>
        protected virtual void OnRpcExecute(Client client, IRpcModel model) => OnRpcInvoke(client, model);
        #endregion

        /// <summary>
        /// 运行服务器
        /// </summary>
        /// <param name="port">服务器端口号</param>
        public void Run(int port = 9543) => Start(port);

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="port">端口</param>
        public override void Start(int port = 9543)
        {
            if (Server != null)//如果服务器套接字已创建
                throw new Exception("服务器已经运行，不可重新启动，请先关闭后在重启服务器");
            Port = port;
            RegisterEvent();
            OnStarting();
            CreateServerSocket(port);
            IsRunServer = true;
            StartSocketHandler();
            StartProcessAccept();
            StartLoopEvent();
            StartProcessThreads();
            OnStartupCompleted();
#if WINDOWS
            Win32KernelAPI.timeBeginPeriod(1);
#endif
            AddRpc(this);
        }

        protected virtual void StartLoopEvent()
        {
            ThreadManager.Invoke("DataTrafficHandler", 1f, DataTrafficHandler);
        }

        protected virtual void StartProcessAccept() { }

        protected virtual void CreateServerSocket(int port) { }

        protected virtual void StartProcessThreads()
        {
            ThreadPool = new ThreadPipeline<Client>
            {
                MaxThread = Config.MaxThread
            };
            ThreadPool.SetProcess<Client>(NetworkProcessing);
            ThreadPool.Init("NetworkProcessing_");
            ThreadPool.Start();
        }

        protected void RegisterEvent()
        {
            OnConnecting += OnHasConnect;
            OnDisconnect += OnRemoveClient;
            OnRpcInvoke ??= RpcAdapter.Enqueue;
        }

        /// <summary>
        /// 流量统计线程
        /// </summary>
        protected virtual bool DataTrafficHandler()
        {
            try
            {
                trafficStatistics.TotalSentBytes += trafficStatistics.SentBytesPerSecond;
                trafficStatistics.TotalReceivedBytes += trafficStatistics.ReceivedBytesPerSecond;
                var entities = new FrameRateEntity[ThreadPool.GroupCount];
                for (int i = 0; i < ThreadPool.GroupCount; i++)
                    entities[i] = new FrameRateEntity(ThreadPool.GetGroup(i).Id, ThreadPool.GetGroup(i).FPS);
                trafficStatistics.FrameRates = entities;
                OnNetworkDataTraffic?.Invoke(trafficStatistics);
            }
            catch (Exception ex)
            {
                NDebug.LogError("流量统计异常:" + ex);
            }
            finally
            {
                trafficStatistics.SentBytesPerSecond = 0;
                trafficStatistics.SentPacketsPerSecond = 0;
                trafficStatistics.RpcResolveCount = 0;
                trafficStatistics.ReceivedPacketsPerSecond = 0;
                trafficStatistics.ReceivedBytesPerSecond = 0;
                for (int i = 0; i < ThreadPool.GroupCount; i++)
                    ThreadPool.GetGroup(i).FPS = 0;
            }
            return IsRunServer;
        }

        /// <summary>
        /// 开始接收数据
        /// </summary>
        protected virtual void StartSocketHandler() { }

        /// <summary>
        /// 网络处理线程组
        /// </summary>
        /// <param name="group"></param>
        protected virtual void NetworkProcessing(ThreadGroup<Client> group)
        {
            for (int i = 0; i < group.Workers.Count; i++)
            {
                var client = group.Workers[i];
                client.NetworkUpdate();
            }
            RpcAdapter.Execute();
            group.FPS++;
        }

        protected virtual Client AcceptHander(Socket clientSocket, EndPoint remotePoint, params object[] args)
        {
            var client = new Client
            {
                Client = clientSocket,
                RemotePoint = remotePoint,
                ConnectTime = DateTime.Now,
                Connected = true,
                Server = this,
                UserId = remotePoint.GetRemoteId(),
                QueueUp = QueueUp,
                SerializeAdapter = SerializeAdapter,
                PackageAdapter = PackageAdapter,
                RpcAdapter = RpcAdapter.Copy(), //要复制新的实例出来，否则会共用导致无法区分是Client的Rpc还是Server的Rpc
                performance = performance,
                receiveHandlerInternal = ReceiveHandlerInternal,
                trafficStatistics = trafficStatistics,
                config = Config,
            };
            client.NetworkInitialize();
            AcceptHander(client, args);
            OnThreadQueueSet(client);
            client.SetClientIdentity();//此处发的identity是连接时的标识, 还不是开发者自定义的标识
            Clients.TryAdd(remotePoint.GetRemoteId(), client);//之前放在上面, 由于接收线程并行, 还没赋值revdQueue就已经接收到数据, 导致提示内存池泄露
            OnHasConnect(client);
            if (Clients.Count >= Config.OnlineLimit + Config.LineUp)
            {
                client.Call(NetCmd.ServerFull, new byte[1]);
                client.SendDirect();
                client.Connected = false;
                client.QueueUpNo = int.MaxValue;
            }
            else if (Clients.Count > Config.OnlineLimit)
            {
                QueueUp.Enqueue(client);
                client.QueueUpNo = QueueUp.Count;
                var segment = BufferPool.Take(50);
                segment.Write(QueueUp.Count);
                segment.Write(client.QueueUpNo);
                client.Call(NetCmd.QueueUp, segment.ToArray(true));
            }
            return client;
        }

        protected virtual void OnThreadQueueSet(Client client)
        {
            client.Group = ThreadPool.SelectGroup();
        }

        protected virtual void AcceptHander(Client client, params object[] args) { }

        internal void ReceiveHandlerInternal(ClientPeer client, IRpcModel model, ISegment segment) => OnReceiveDataHandler(client as Client, model, segment);
        protected virtual void OnReceiveDataHandler(Client client, IRpcModel model, ISegment segment)
        {
            if (model.Protocol != 0 && (model.Cmd == NetCmd.CallRpcHash | model.Cmd == NetCmd.SafeCallHash))
                OnRpcExecute(client, model);
            else
                OnReceiveData?.Invoke(client, model);
        }

        /// <inheritdoc/>
        public override void RemoveClient(EndPoint remotePoint)
        {
            if (!Clients.TryRemove(remotePoint.GetRemoteId(), out var client))//防止两次进入
                return;
            OnRemoveClient(client);
            client.OnRemoveClient();
            client.Dispose();
            if (client.IsQueueUp)
                return;
            while (QueueUp.TryDequeue(out var client1))
            {
                if (client1.IsDispose)
                    continue;
                if (!client1.Connected)
                    continue;
                client1.QueueUpNo = 0;
                client1.Call(NetCmd.QueueCancellation, new byte[1]);
                break;
            }
        }

        /// <summary>
        /// 关闭服务器
        /// </summary>
        public virtual void Close()
        {
            IsRunServer = false;
            foreach (var client in Clients.Values)
                client.Dispose();
            Clients.Clear();
            Thread.Sleep(50);//等待线程退出后再关闭套接字, 解决在revd方法出错
            if (Server != null)
            {
                Server.Dispose();
                Server.Close();
                Server = null;
            }
            if (ServerArgs != null)
            {
                ServerArgs.Dispose();
                ServerArgs = null;
                #region 清除iocp完成端口绑定事件池缓存
                var overlappedType = typeof(Overlapped);
                var overlappedDataCacheField = overlappedType.GetField("s_overlappedDataCache", BindingFlags.NonPublic | BindingFlags.Static);
                var s_overlappedDataCache = overlappedDataCacheField.GetValue(null);
                var overlappedDataCacheType = s_overlappedDataCache.GetType();
                var m_FreeListField = overlappedDataCacheType.GetField("m_FreeList", BindingFlags.NonPublic | BindingFlags.Instance);
                var m_FreeList = m_FreeListField.GetValue(s_overlappedDataCache) as ConcurrentStack<object>;
                m_FreeList.Clear();
                var m_NotGen2Field = overlappedDataCacheType.GetField("m_NotGen2", BindingFlags.NonPublic | BindingFlags.Instance);
                var m_NotGen2 = m_NotGen2Field.GetValue(s_overlappedDataCache) as List<object>;
                m_NotGen2.Clear();
                #endregion
            }
            foreach (var item in ServerThreads)
                item.Value.Interrupt();
            ServerThreads.Clear();
            ThreadPool.Dispose();
            OnConnecting -= OnHasConnect;
            OnDisconnect -= OnRemoveClient;
            NDebug.Log("服务器已关闭！");
        }

        public void Call(Client client, uint protocol, params object[] pars)
            => Call(client, NetCmd.CallRpc, protocol, true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Call(Client client, NetCmd cmd, uint protocol, params object[] pars)
            => Call(client, cmd, protocol, true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Response(Client client, uint protocol, SyncSerialize serialize, in RequestToken token, params object[] pars)
            => Call(client, NetCmd.CallRpc, protocol, true, serialize, token, null, pars);
        public void Response(Client client, uint protocol, in RequestToken token, params object[] pars)
            => Call(client, NetCmd.CallRpc, protocol, true, SyncSerialize.False, token, null, pars);
        public void Response(Client client, NetCmd cmd, uint protocol, in RequestToken token, params object[] pars)
            => Call(client, cmd, protocol, true, SyncSerialize.False, token, null, pars);

        public void Call(Client client, string func, params object[] pars)
            => Call(client, NetCmd.CallRpc, func.CRCU32(), true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Call(Client client, NetCmd cmd, string func, params object[] pars)
            => Call(client, cmd, func.CRCU32(), true, SyncSerialize.False, RequestToken.Default, null, pars);
        public void Response(Client client, string func, SyncSerialize serialize, in RequestToken token, params object[] pars)
            => Call(client, NetCmd.CallRpc, func.CRCU32(), true, serialize, token, null, pars);
        public void Response(Client client, string func, in RequestToken token, params object[] pars)
            => Call(client, NetCmd.CallRpc, func.CRCU32(), true, SyncSerialize.False, token, null, pars);
        public void Response(Client client, NetCmd cmd, string func, in RequestToken token, params object[] pars)
            => Call(client, cmd, func.CRCU32(), true, SyncSerialize.False, token, null, pars);

        public void Call(Client client, NetCmd cmd, uint protocol, SyncSerialize serialize, in RequestToken token, params object[] pars)
            => Call(client, cmd, protocol, true, serialize, token, null, pars);

        public void Call(Client client, byte[] buffer) => Call(client, NetCmd.Other, 0, false, SyncSerialize.False, RequestToken.Default, buffer);
        public void Call(Client client, NetCmd cmd, byte[] buffer) => Call(client, cmd, 0, false, SyncSerialize.False, RequestToken.Default, buffer);

        public void Call(Client client, NetCmd cmd, byte[] buffer, bool kernel, SyncSerialize serialize) => Call(client, cmd, 0, kernel, serialize, RequestToken.Default, buffer);
        public void Call(Client client, NetCmd cmd, uint protocol, bool kernel, SyncSerialize serialize, in RequestToken token, byte[] buffer, params object[] pars)
            => client.Call(cmd, protocol, kernel, serialize, token, buffer, pars);

        /// <inheritdoc/>
        public void Multicast(IEnumerable<Client> clients, byte[] buffer)
            => Multicast(clients, NetCmd.Other, buffer);
        /// <inheritdoc/>
        public void Multicast(IEnumerable<Client> clients, NetCmd cmd, byte[] buffer)
            => Multicast(clients, RpcUnitFactory.Create(cmd: cmd.value, kernel: false, buffer: buffer, serialize: SyncSerialize.False));
        /// <inheritdoc/>
        public void Multicast(IEnumerable<Client> clients, NetCmd cmd, byte[] buffer, bool kernel, SyncSerialize serialize)
            => Multicast(clients, RpcUnitFactory.Create(cmd: cmd.value, kernel: kernel, buffer: buffer, serialize: serialize));
        /// <inheritdoc/>
        public void Multicast(IEnumerable<Client> clients, uint protocol, params object[] pars)
            => Multicast(clients, NetCmd.CallRpc, protocol, pars);
        /// <inheritdoc/>
        public void Multicast(IEnumerable<Client> clients, NetCmd cmd, uint protocol, params object[] pars)
            => Multicast(clients, RpcUnitFactory.Create(cmd: cmd.value, kernel: true, protocol: protocol, pars: pars));
        public void Multicast(IEnumerable<Client> clients, string func, params object[] pars)
            => Multicast(clients, NetCmd.CallRpc, func, pars);
        public void Multicast(IEnumerable<Client> clients, NetCmd cmd, string func, params object[] pars)
            => Multicast(clients, RpcUnitFactory.Create(cmd: cmd.value, kernel: true, protocol: func.CRCU32(), pars: pars));
        public virtual void Multicast(IEnumerable<Client> clients, IRpcModel model)
        {
            if (model.Buffer == null)
            {
                model.Serialize = false;
                var segment = BufferPool.Take();
                SerializeAdapter.OnSerializeRpc(segment, model);
                model.Buffer = segment.ToArray(true);
            }
            if (model.Buffer.Length / Config.MTU > Config.LimitQueueCount)
            {
                NDebug.LogError("Multocast数据太大，请分块发送!");
                return;
            }
            var enumerator = clients.GetEnumerator();
            while (enumerator.MoveNext())
            {
                var client = enumerator.Current;
                if (client == null)
                    continue;
                client.Call(model);
            }
        }

        /// <summary>
        /// 设置心跳时间
        /// </summary>
        /// <param name="timeout">心跳时间间隔, 每interval毫秒会检测一次</param>
        public void SetKeepAliveTimeout(int timeout)
        {
            Config.KeepAliveTimeout = timeout;
            foreach (var client in Clients.Values)
            {
                client.SetKeepAliveTimeout(Config.KeepAliveTimeout);
            }
        }

        /// <summary>
        /// 检查断线重连处理
        /// </summary>
        /// <param name="client"></param>
        /// <param name="segment"></param>
        /// <param name="args"></param>
        protected virtual Client CheckReconnect(Socket client, ISegment segment, params object[] args)
        {
            client.ReceiveTimeout = 0;
            var userID = segment.ReadInt64();
            if (Clients.TryGetValue(userID, out var session))
            {
                session.ReconnectTimeout = (uint)Environment.TickCount + Config.ReconnectionTimeout;
                var newRemotePoint = client.RemoteEndPoint as IPEndPoint;
                var oldRemotePoint = session.RemotePoint as IPEndPoint;
                //防止出错或者假冒的客户端设置, 导致直接替换真实的客户端
                //如果是新的IP和旧的IP相同，是可以进行替换的，这样就不会发生假冒客户端问题
                //Equals里面有重写IPAddress.Equals，所以能判断出来
                if (!session.Connected | !session.Client.Connected | Equals(oldRemotePoint.Address, newRemotePoint.Address))
                {
                    session.Client = client;
                    session.Connected = true;
                    AcceptHander(session, args); //需要重新设置断线重连的新套接字对象
                    session.SetClientIdentity();
                    session.OnReconnecting();
                    OnReconnecting(session);
                    return session;
                }
            }
            return AcceptHander(client, client.RemoteEndPoint, args);//如果取出的客户端不断线, 那说明是客户端有问题或者错乱, 给他个新的连接
        }

        public override string ToString()
        {
            return $"{Name} {HostName}";
        }
    }

    public abstract partial class ServerBase<Client, Scene> : ServerBase<Client>
        where Client : ClientPeerBase, new()
        where Scene : SceneBase<Client>, new()
    {
        #region 属性
        /// <summary>
        /// 登录的客户端, 键为<see cref="ClientPeerBase.PlayerId"/>
        /// </summary>
        public ConcurrentDictionary<object, Client> Players { get; private set; } = new();
        /// <summary>
        /// 服务器场景，key是场景名或房间名，关卡名。 value是(场景或房间，关卡等)对象
        /// </summary>
        public ConcurrentDictionary<string, Scene> Scenes { get; set; } = new();
        /// <summary>
        /// 服务器主场景名称
        /// </summary>
        public string MainSceneName { get; set; } = "World";
        /// <summary>
        /// 网络场景同步时间(帧同步间隔), 默认每33毫秒同步一次, 一秒同步30次, 可自己设置
        /// </summary>
        public uint SceneTick { get; set; } = 33;
        /// <summary>
        /// 获取登录游戏的玩家在线人数
        /// </summary>
        public int OnlinePlayers => Players.Count;
        /// <summary>
        /// 获取未登录的玩家在线人数
        /// </summary>
        public int OnlineUnPlayers => Clients.Count - Players.Count;
        #endregion

        #region 事件
        #endregion

        /// <inheritdoc/>
        public override void Start(int port = 9543)
        {
            base.Start(port);
            OnAddDefaultScene();
        }

        /// <summary>
        /// 当未知客户端发送数据请求，返回<see langword="false"/>，不允许<see langword="unClient"/>进入服务器!，如果返回的是<see langword="true"/>，则允许<see langword="unClient"/>客户端进入服务器
        /// 同时会将<see langword="unClient"/>添加到<see cref="Players"/>和<see cref="UIDClients"/>在线字典中.
        /// <code>客户端玩家的入口点，在这里可以控制客户端是否可以进入服务器与其他客户端进行网络交互</code>
        /// 在这里可以用来判断客户端登录和注册等等进站许可 (默认是允许进入服务器)
        /// </summary>
        /// <param name="unClient">尚未登录的客户端对象</param>
        /// <param name="model">数据模型</param>
        /// <returns></returns>
        protected virtual bool OnUnClientRequest(Client unClient, IRpcModel model) => true;

        protected override void StartProcessThreads()
        {
            ThreadPool = new ThreadPipeline<Client, Scene>
            {
                MaxThread = Config.MaxThread
            };
            ThreadPool.SetProcess<Client, Scene>(NetworkProcessing);
            ThreadPool.Init("NetworkProcessing_");
            ThreadPool.Start();
        }

        protected override void OnThreadQueueSet(Client client)
        {
            client.Group = ThreadPool.SelectGroup();
        }

        protected void NetworkProcessing(ThreadGroup<Client, Scene> group)
        {
            for (int i = 0; i < group.Workers1.Count; i++)
            {
                var client = group.Workers1[i];
                client.NetworkUpdate();
            }
            RpcAdapter.Execute();
            for (int i = 0; i < group.Workers2.Count; i++)
            {
                var scene = group.Workers2[i];
                scene.Tick();
            }
            group.FPS++;
        }

        /// <inheritdoc/>
        protected override void OnReceiveDataHandler(Client client, IRpcModel model, ISegment segment)
        {
            if (!client.IsLogin)
            {
                if (OnUnClientRequest(client, model))//当有客户端连接时,如果允许用户添加此客户端
                    LoginHandler(client);
                return;
            }
            switch (model.Cmd)
            {
                case NetCmd.SceneHash:
                    OnSceneRelay(client, model);
                    break;
                case NetCmd.NoticeHash:
                    OnNoticeRelay(client, model);
                    break;
                case NetCmd.OperationSyncHash:
                    var operList = SerializeAdapter.OnDeserializeOpt(segment);
                    OnOperationSync(client, operList);
                    break;
                case NetCmd.SyncVarP2PHash:
                    SyncVarHelper.SyncVarHandler(RpcAdapter.SyncVarDic, model.GetBuffer);
                    break;
                case NetCmd.SyncPropertyDataHash:
                    client.OnPropertySync(model);
                    break;
                default:
                    base.OnReceiveDataHandler(client, model, segment);
                    break;
            }
        }

        /// <summary>
        /// 主动登录服务器, 类似OnUnClientRequest重写方法的返回值为true
        /// </summary>
        /// <param name="client"></param>
        protected void LoginHandler(Client client)
        {
            if (!client.IsLogin)
            {
                client.IsLogin = true;
                LoginInternal(client);
            }
        }

        private void LoginInternal(Client client)
        {
            //如果一个账号快速登录断开,再登录断开,心跳检查断线会延迟,导致无法移除掉已在游戏的客户端对象
            //如果此账号的玩家已经登录游戏, 则会先进行退出登录, 此客户端才能登录进来
            //注意:如果移除了自己,就不需要调用退出登录处理
            if (Players.TryRemove(client.PlayerId, out var client1) & client1 != client)
                SignOutInternal(client1, true);
            //当此玩家一直从登录到被退出登录, 再登录后PlayerID被清除了, 如果是这种情况下, 开发者也没有给PlayerID赋值, 那么默认就需要给uid得值
            if (client.PlayerId == DBNull.Value)
                client.PlayerId = client.UserId;
            if (string.IsNullOrEmpty(client.Name))
                client.Name = client.RemotePoint.ToString();
            Players[client.PlayerId] = client;
            client.OnStart();
            client.RpcAdapter.AddRpc(client);
            client.SetClientIdentity();//将发送登录成功的identity标识, 开发者可赋值, 必须保证是唯一的
            OnAddPlayerToScene(client);
        }

        /// <summary>
        /// playerID玩家是否在线?
        /// </summary>
        /// <param name="playerID"></param>
        /// <returns></returns>
        public virtual bool IsOnline(object playerID)
        {
            return IsOnline(playerID, out _);
        }

        /// <summary>
        /// playerID玩家是否在线? 并且如果在线则out 在线玩家的对象
        /// </summary>
        /// <param name="playerID"></param>
        /// <param name="client"></param>
        /// <returns></returns>
        public virtual bool IsOnline(object playerID, out Client client)
        {
            return Players.TryGetValue(playerID, out client);
        }

        /// <summary>
		/// 强制下线处理, 将client客户端从在线字段<see cref="Players"/>和<see cref="UIDClients"/>和<see cref="AllClients"/>字段中移除
		/// </summary>
		/// <param name="client"></param>
		public virtual void OfflineHandle(Client client)
        {
            client.SendDirect();
            client.RemoveClient();
            NDebug.Log("[" + client.Name + "]被强制下线...!");
        }

        /// <summary>
        /// 退出登录, 将client客户端从在线字段<see cref="Players"/>中移除
        /// </summary>
        /// <param name="client"></param>
        public virtual void SignOut(Client client)
        {
            if (!client.IsLogin)
                return;
            if (Players.TryRemove(client.PlayerId, out _))
                SignOutInternal(client, false);
        }

        protected void SignOutInternal(Client client, bool passive)
        {
            client.SendDirect();
            ExitScene(client, false);
            OnSignOut(client, passive);
            client.OnSignOut();
            client.IsLogin = false;
            client.PlayerId = DBNull.Value;//此处必须清除,要不然当移除断线的账号后, 就会移除掉新登录的此账号在线字段Players
            NDebug.Log("[" + client.Name + "]退出登录...!");
        }

        /// <summary>
        /// 当客户端退出登录, 如果两个账号同时登录或者在心跳时间还没到检测时另外一个玩家也登录了相同的账号, 则会强制退出上一个账号的登录
        /// </summary>
        /// <param name="client"></param>
        /// <param name="passive">passive为true则为被动挤出登录, false为主动退出登录</param>
        public virtual void OnSignOut(Client client, bool passive)
        {
        }

        #region 重写方法
        /// <summary>
        /// 当添加默认网络场景，服务器初始化后会默认创建一个主场景，供所有玩家刚登陆成功分配的临时场景，默认初始化场景人数为1000人
        /// </summary>
        /// <returns>返回值string：网络玩家所在的场景名称 , 返回值NetScene：网络玩家的场景对象</returns>
        protected virtual void OnAddDefaultScene()
        {
            CreateScene(MainSceneName, new Scene() { Capacity = 1000 });
        }

        /// <summary>
        /// 当添加玩家到默认场景， 如果不想添加刚登录游戏成功的玩家进入主场景，可重写此方法让其失效
        /// </summary>
        /// <param name="client"></param>
        protected virtual void OnAddPlayerToScene(Client client)
        {
            if (Scenes.TryGetValue(MainSceneName, out Scene scene))
                scene.AddPlayer(client);//将网络玩家添加到主场景集合中
        }

        /// <summary>
        /// 当接收到客户端使用<see cref="Client.ClientBase.AddOperation(Operation)"/>方法发送的请求时调用
        /// </summary>
        /// <param name="client">当前客户端</param>
        /// <param name="operList">操作列表</param>
        protected virtual void OnOperationSync(Client client, in OperationList operList)
        {
            if (client.OnOperationSync(operList))
                return;
            if (client.Scene is Scene scene)
                scene.OnOperationSync(client, operList);
        }

        /// <summary>
        /// 当客户端使用场景转发命令会调用此方法
        /// </summary>
        /// <param name="client"></param>
        /// <param name="model"></param>
        protected virtual void OnSceneRelay(Client client, IRpcModel model)
        {
            if (client.Scene is not Scene scene)
            {
                client.Call(RpcUnitFactory.Create(cmd: model.Cmd, kernel: model.Kernel, buffer: model.GetBuffer, serialize: SyncSerialize.False, protocol: model.Protocol));
                return;
            }
            Multicast(scene.Clients, RpcUnitFactory.Create(cmd: model.Cmd, kernel: model.Kernel, buffer: model.GetBuffer, serialize: SyncSerialize.False, protocol: model.Protocol));
        }

        /// <summary>
        /// 当客户端使用公告转发命令会调用此方法
        /// </summary>
        /// <param name="client"></param>
        /// <param name="model"></param>
        protected virtual void OnNoticeRelay(Client client, IRpcModel model)
        {
            Multicast(Clients.Values, RpcUnitFactory.Create(cmd: model.Cmd, kernel: model.Kernel, buffer: model.GetBuffer, serialize: SyncSerialize.False, protocol: model.Protocol, token: model.Token));
        }
        #endregion

        public override void RemoveClient(EndPoint remotePoint)
        {
            if (!Clients.TryRemove(remotePoint.GetRemoteId(), out var client))//防止两次进入
                return;
            Players.TryRemove(client.PlayerId, out _);
            ExitScene(client, false);
            OnRemoveClient(client);
            client.OnRemoveClient();
            OnClientDispose(client);
            client.Dispose();
            if (client.IsQueueUp)
                return;
            while (QueueUp.TryDequeue(out var client1))
            {
                if (client1.IsDispose)
                    continue;
                if (!client1.Connected)
                    continue;
                client1.QueueUpNo = 0;
                client1.Call(NetCmd.QueueCancellation, new byte[1]);
                break;
            }
        }

        protected virtual void OnClientDispose(Client client) { }

        #region 场景API
        /// <summary>
        /// 创建网络场景, 退出当前场景,进入所创建的场景 - 创建场景成功返回场景对象， 创建失败返回null
        /// </summary>
        /// <param name="client">创建网络场景的玩家实体</param>
        /// <param name="name">要创建的场景号或场景名称</param>
        /// <returns></returns>
        public Scene CreateScene(Client client, string name) => CreateScene(client, name, new Scene() { Name = name });

        /// <summary>
        /// 创建网络场景, 退出当前场景并加入所创建的场景 - 创建场景成功返回场景对象， 创建失败返回null
        /// </summary>
        /// <param name="client">创建网络场景的玩家实体</param>
        /// <param name="scene">创建场景的实体</param>
        /// <returns></returns>
        public Scene CreateScene(Client client, Scene scene) => CreateScene(client, scene.Name, scene);

        /// <summary>
        /// 创建网络场景, 退出当前场景并加入所创建的场景 - 创建场景成功返回场景对象， 创建失败返回null
        /// </summary>
        /// <param name="client">创建网络场景的玩家实体</param>
        /// <param name="name">要创建的场景号或场景名称</param>
        /// <param name="scene">创建场景的实体</param>
        /// <returns></returns>
        public Scene CreateScene(Client client, string name, Scene scene)
        {
            scene.Name = name;
            return CreateScene(client, scene, out _);
        }

        public Scene CreateScene(Client client, Scene scene, out Scene oldScene)
        {
            oldScene = client.Scene as Scene;
            if (string.IsNullOrEmpty(scene.Name))
                return null;
            if (Scenes.TryAdd(scene.Name, scene))
            {
                if (oldScene != null)
                    oldScene.Remove(client);
                scene.Server = this;
                scene.InitEvent(SceneTick);
                OnSceneGroupSet(scene);
                scene.AddPlayer(client);
                return scene;
            }
            return null;
        }

        /// <summary>
        /// 创建一个场景, 成功则返回场景对象, 创建失败则返回null
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Scene CreateScene(string name) => CreateScene(name, new Scene());

        /// <summary>
        /// 创建一个场景, 成功则返回场景对象, 创建失败则返回null
        /// </summary>
        /// <param name="name"></param>
        /// <param name="scene"></param>
        /// <returns></returns>
        public Scene CreateScene(string name, Scene scene)
        {
            scene.Name = name;
            return CreateScene(scene);
        }

        /// <summary>
        /// 创建一个场景, 成功则返回场景对象, 创建失败则返回null
        /// </summary>
        /// <param name="scene"></param>
        /// <returns></returns>
        public Scene CreateScene(Scene scene)
        {
            if (string.IsNullOrEmpty(scene.Name))
            {
                NDebug.LogError("创建的场景必须给名称,场景名称必须是唯一的!");
                return null;
            }
            if (Scenes.TryAdd(scene.Name, scene))
            {
                scene.Server = this;
                scene.InitEvent(SceneTick);
                OnSceneGroupSet(scene);
                return scene;
            }
            return null;
        }

        protected virtual void OnSceneGroupSet(Scene scene)
        {
            scene.Group = ThreadPool.SelectGroup();
        }

        /// <summary>
        /// 退出当前场景,加入指定的场景 - 成功进入返回场景对象，进入失败返回null
        /// </summary>
        /// <param name="client">要进入sceneID场景的玩家实体</param>
        /// <param name="name">场景ID，要切换到的场景号或场景名称</param>
        /// <returns></returns>
        public Scene JoinScene(Client client, string name) => SwitchScene(client, name);

        public Scene JoinScene(Client client, Scene scene) => SwitchScene(client, scene);

        /// <summary>
        /// 进入场景 - 成功进入返回true，进入失败返回false
        /// </summary>
        /// <param name="client">要进入sceneID场景的玩家实体</param>
        /// <param name="name">场景ID，要切换到的场景号或场景名称</param>
        /// <returns></returns>
        public Scene EnterScene(Client client, string name) => SwitchScene(client, name);

        public Scene EnterScene(Client client, Scene scene) => SwitchScene(client, scene);

        /// <summary>
        /// 切换场景
        /// </summary>
        /// <param name="client">要操作的玩家</param>
        /// <param name="name">场景名称</param>
        /// <returns>进入的场景,如果查询的场景不存在则为null</returns>
        public Scene SwitchScene(Client client, string name) => SwitchScene(client, name, out _);

        /// <summary>
        /// 切换场景
        /// </summary>
        /// <param name="client">要操作的玩家</param>
        /// <param name="name">场景名称</param>
        /// <param name="oldScene">上次所在的场景</param>
        /// <returns>进入的场景,如果查询的场景不存在则为null</returns>
        public Scene SwitchScene(Client client, string name, out Scene oldScene)
        {
            oldScene = client.Scene as Scene;
            if (string.IsNullOrEmpty(name))
                return null;
            if (oldScene != null)
                if (oldScene.Name == name) //如果已经在这个场景, 直接返回对象
                    return oldScene;
            if (Scenes.TryGetValue(name, out Scene scene1))
                return SwitchScene(client, scene1, out _);
            return null;
        }

        /// <summary>
        /// 切换场景
        /// </summary>
        /// <param name="client">要操作的玩家</param>
        /// <param name="enterScene">要进入的场景</param>
        /// <returns>进入的场景</returns>
        public Scene SwitchScene(Client client, Scene enterScene) => SwitchScene(client, enterScene, out _);

        /// <summary>
        /// 切换场景
        /// </summary>
        /// <param name="player">要操作的玩家</param>
        /// <param name="enterScene">要进入的场景</param>
        /// <param name="oldScene">上次所在的场景</param>
        /// <returns>进入的场景</returns>
        public Scene SwitchScene(Client player, Scene enterScene, out Scene oldScene)
        {
            oldScene = player.Scene as Scene;
            if (oldScene != null)
                oldScene.Remove(player);
            enterScene.AddPlayer(player);
            return enterScene;
        }

        /// <summary>
        /// 退出场景 exitCurrentSceneCall回调时已经不包含player对象
        /// </summary>
        /// <param name="player"></param>
        /// <param name="isEntMain">退出当前场景是否进入主场景: 默认进入主场景</param>
        /// <param name="exitCurrentSceneCall">即将退出当前场景的处理委托函数: 如果你需要对即将退出的场景进行一些事后处理, 则在此委托函数执行! 如:退出当前场景通知当前场景内的其他客户端将你的玩家对象移除等功能</param>
        public void ExitScene(Client player, bool isEntMain = true, Action<Scene> exitCurrentSceneCall = null)
            => RemoveScenePlayer(player, isEntMain, exitCurrentSceneCall);

        /// <summary>
        /// 移除服务器场景. 从服务器总场景字典中移除指定的场景: 当你移除指定场景后,如果场景内有其他玩家在内, 则把其他玩家添加到主场景内
        /// </summary>
        /// <param name="name">要移除的场景id</param>
        /// <param name="addToMainScene">允许即将移除的场景内的玩家添加到主场景?</param>
        /// <param name="exitCurrentSceneCall">即将退出当前场景的处理委托函数: 如果你需要对即将退出的场景进行一些事后处理, 则在此委托函数执行! 如:退出当前场景通知当前场景内的其他客户端将你的玩家对象移除等功能</param>
        /// <returns></returns>
        public bool RemoveScene(string name, bool addToMainScene = true, Action<Scene> exitCurrentSceneCall = null)
        {
            if (Scenes.TryRemove(name, out Scene scene))
            {
                exitCurrentSceneCall?.Invoke(scene);
                if (addToMainScene)
                {
                    var mainScene = Scenes[MainSceneName];
                    if (mainScene != null)
                        mainScene.AddPlayers(scene.Clients);
                }
                scene.RemoveScene();
                return true;
            }
            return false;
        }

        /// <summary>
        /// 将玩家从当前所在的场景移除掉， 移除之后此客户端将会进入默认主场景 call回调时已经不包含player对象
        /// </summary>
        /// <param name="client">要执行的玩家实体</param>
        /// <param name="isEntMain">退出当前场景是否进入主场景: 默认进入主场景</param>
        /// <param name="exitCurrentSceneCall">即将退出当前场景的处理委托函数: 如果你需要对即将退出的场景进行一些事后处理, 则在此委托函数执行! 如:退出当前场景通知当前场景内的其他客户端将你的玩家对象移除等功能</param>
        /// <returns></returns>
        public bool RemoveScenePlayer(Client client, bool isEntMain = true, Action<Scene> exitCurrentSceneCall = null)
        {
            if (string.IsNullOrEmpty(client.SceneName))
                return false;
            if (Scenes.TryGetValue(client.SceneName, out Scene scene))
            {
                scene.Remove(client);
                exitCurrentSceneCall?.Invoke(scene);
                if (isEntMain)
                {
                    Scene mainScene = Scenes[MainSceneName];
                    mainScene.AddPlayer(client);
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 场景是否存在?
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public bool IsHasScene(string name) => Scenes.ContainsKey(name);
        #endregion
    }


    public abstract partial class ServerUdpBase<Client, Scene> : ServerBase<Client, Scene>
        where Client : ClientPeerBase, new()
        where Scene : SceneBase<Client>, new()
    {
        protected override void CreateServerSocket(int port)
        {
            Server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
            {
                SendBufferSize = Config.SendBufferSize,
                ReceiveBufferSize = Config.ReceiveBufferSize
            };
            var address = new IPEndPoint(IPAddress.Any, port);
            Server.Bind(address);
            try //在安卓启动服务器时忽略此错误, 或者其他平台错误
            {
                uint IOC_IN = 0x80000000;
                uint IOC_VENDOR = 0x18000000;
                uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
                Server.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);//udp远程关闭现有连接方案
            }
            catch (Exception ex)
            {
                NDebug.LogWarning(ex);
            }
        }

        protected override void StartSocketHandler()
        {
            BeginReceive();
        }

        private void BeginReceive()
        {
            try
            {
                if (!IsRunServer)
                    return;
                var segment = BufferPool.Take(Config.ReceiveBufferSize);
                var remote = Server.LocalEndPoint;
                Server.BeginReceiveFrom(segment.Buffer, 0, segment.Length, SocketFlags.None, ref remote, EndReceive, segment);
            }
            catch (Exception ex)
            {
                NDebug.LogError(ex);
            }
        }

        private void EndReceive(IAsyncResult ar)
        {
            try
            {
                var remote = Server.LocalEndPoint;
                var count = Server.EndReceiveFrom(ar, ref remote);
                var segment = ar.AsyncState as ISegment;
                segment.Count = count;
                if (!Clients.TryGetValue(remote.GetRemoteId(), out var client))
                    Clients.TryAdd(remote.GetRemoteId(), client = CheckReconnect(remote, segment));
                client.ReceiveList.Enqueue(segment);
            }
            catch (Exception ex)
            {
                NDebug.LogError(ex);
            }
            BeginReceive();
        }

        protected virtual Client CheckReconnect(EndPoint remotePoint, ISegment segment, params object[] args)
        {
            var userID = segment.ReadInt64();
            if (Clients.TryGetValue(userID, out var session))
            {
                session.ReconnectTimeout = (uint)Environment.TickCount + Config.ReconnectionTimeout;
                var newRemotePoint = remotePoint as IPEndPoint;
                var oldRemotePoint = session.RemotePoint as IPEndPoint;
                //防止出错或者假冒的客户端设置, 导致直接替换真实的客户端
                //如果是新的IP和旧的IP相同，是可以进行替换的，这样就不会发生假冒客户端问题
                //Equals里面有重写IPAddress.Equals，所以能判断出来
                if (!session.Connected | Equals(oldRemotePoint.Address, newRemotePoint.Address))
                {
                    session.RemotePoint = newRemotePoint;
                    session.Connected = true;
                    session.UserId = newRemotePoint.GetRemoteId();
                    AcceptHander(session, args); //需要重新设置断线重连的新套接字对象
                    session.SetClientIdentity();
                    session.OnReconnecting();
                    OnReconnecting(session);
                    Clients.TryRemove(userID, out session); //移除旧的key
                    return session;
                }
            }
            return AcceptHander(null, remotePoint, args);//如果取出的客户端不断线, 那说明是客户端有问题或者错乱, 给他个新的连接
        }
    }
}